#ifndef __image_helper_h__
#define __image_helper_h__

#include <windows.h>
#include <tchar.h>
#include <gdiplus.h>
#include <atlbase.h>

#pragma comment(lib, "gdiplus.lib")

class ImageHelper
{
public:
    enum ImageType
    {
        enumBmp,
        enumJpeg,
        enumPng
    };

    static int GetEncoderClsid( LPCWSTR lpszFormat, CLSID* pClsid )
    {
        UINT num = 0;
        UINT size = 0;

        Gdiplus::ImageCodecInfo* pImageCodecInfo= NULL;

        Gdiplus::GetImageEncodersSize( &num, &size );
        if( size == 0 )
        {
            return -1;
        }

        pImageCodecInfo = (Gdiplus::ImageCodecInfo*)( malloc(size) );
        if( pImageCodecInfo == NULL )
        {
            return -1;
        }

        Gdiplus::GetImageEncoders( num, size, pImageCodecInfo );

        for( UINT j = 0; j < num; ++j )
        {
            if( wcscmp( pImageCodecInfo[j].MimeType, lpszFormat ) == 0 )
            {
                *pClsid = pImageCodecInfo[j].Clsid;
                free( pImageCodecInfo );
                return j;
            }
        }

        free( pImageCodecInfo );
        return -1;
    }

    static BOOL SaveImageToFile( Gdiplus::Image * pImage, ImageType eType, LPCWSTR lpszPath )
    {
        if( !pImage || !lpszPath )
        {
            return FALSE;
        }

        BOOL bRet = FALSE;
        LPCWSTR lpszType = L"image/bmp";

        CLSID imgClsid;
        switch( eType )
        {
        case enumBmp:
            lpszType = L"image/bmp";
            break;
        case enumJpeg:
            lpszType = L"image/jpeg";
            break;
        case enumPng:
            lpszType = L"image/png";
            break;
        default:
            break;
        }

        if ( -1 != GetEncoderClsid(lpszType, &imgClsid) &&
             Gdiplus::Ok == pImage->Save( lpszPath, &imgClsid, NULL ) )
        {
            bRet = TRUE;
        }

        return bRet;
    }

    static BOOL SaveImageToMemory( Gdiplus::Image * pImage, ImageType eType, BYTE ** pBuffer, DWORD *dwSize )
    {
        if( !pImage || !pBuffer )
        {
            return FALSE;
        }

        *pBuffer = NULL;
        if( dwSize )
            *dwSize = 0;

        LPCWSTR lpszType = L"image/bmp";
        CLSID imgClsid;
        switch( eType )
        {
        case enumBmp:
            lpszType = L"image/bmp";
            break;
        case enumJpeg:
            lpszType = L"image/jpeg";
            break;
        case enumPng:
            lpszType = L"image/png";
            break;
        default:
            break;
        }

        IStream *pStream = NULL;
        BOOL bRet = FALSE;
        do 
        {
            if( CreateStreamOnHGlobal( NULL, TRUE, &pStream ) != S_OK )
                break;

            if( -1 == GetEncoderClsid( lpszType, &imgClsid ) )
                break;

            if( Gdiplus::Ok != pImage->Save( pStream, &imgClsid ) )
                break;

            ULARGE_INTEGER ulnSize;
            LARGE_INTEGER lnOffset;
            lnOffset.QuadPart = 0;

            if( pStream->Seek( lnOffset, STREAM_SEEK_END, &ulnSize ) != S_OK )
                break;

            if( pStream->Seek( lnOffset, STREAM_SEEK_SET, NULL ) != S_OK )
                break;

            *pBuffer = new BYTE[( (size_t)ulnSize.QuadPart ) ];
            if( *pBuffer == NULL )
                break;

            if( dwSize )
                *dwSize = (DWORD)ulnSize.QuadPart;

            ULONG bytesRead;
            if( pStream->Read( *pBuffer, (ULONG)ulnSize.QuadPart, &bytesRead ) != S_OK )
            {
                delete[]( *pBuffer );
                *pBuffer = 0;
                if( dwSize )
                    *dwSize = 0;

                break;
            }
            else
                bRet = TRUE;

        } while (0);

        if( pStream )
        {
            pStream->Release();
        }
        return bRet;
    }

    static BOOL SaveImageToMemory( LPCWSTR lpszPath, BYTE **pBuffer, DWORD *dwSize )
    {
        if( !lpszPath || !pBuffer )
        {
            return FALSE;
        }

        *pBuffer = 0;
        if( dwSize )
            *dwSize = 0;

        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR    pGdiToken;
        GdiplusStartup(&pGdiToken, &gdiplusStartupInput, NULL );
        
        Gdiplus::Image * pImg = Gdiplus::Image::FromFile( lpszPath );
        BOOL bRet = SaveImageToMemory( pImg, enumPng, pBuffer, dwSize );

        Gdiplus::GdiplusShutdown(pGdiToken);
        if( !bRet )
        {
            *pBuffer = 0;
            if( dwSize )
                *dwSize = 0;
        }

        return bRet;
    }

    static BOOL LoadImageFromFile( LPCWSTR lpszPath, Gdiplus::Image **pImage )
    {
        if( !lpszPath || !pImage )
        {
            return FALSE;
        }

        *pImage = 0;
        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR    pGdiToken;
        Gdiplus::GdiplusStartup(&pGdiToken, &gdiplusStartupInput, NULL );

        *pImage = Gdiplus::Image::FromFile( lpszPath );

        Gdiplus::GdiplusShutdown(pGdiToken);

        return TRUE;
    }

    static BOOL LoadImageFromMemory( BYTE * pBuffer, DWORD dwSize, Gdiplus::Image **pImage )
    {
        if( !pBuffer || !pImage )
        {
            return FALSE;
        }

        *pImage = 0;
        HGLOBAL hMemory = ::GlobalAlloc( GMEM_FIXED, dwSize );
        BYTE* pMemData = (BYTE*)::GlobalLock(hMemory);
        memcpy( pMemData, pBuffer, dwSize );

        IStream* pStm = NULL;
        ::CreateStreamOnHGlobal(hMemory, FALSE, &pStm);

        *pImage = Gdiplus::Image::FromStream(pStm);

        ::GlobalUnlock(hMemory);
        pStm->Release();
        ::GlobalFree(hMemory);

        return TRUE;
    }
};

#endif